#region Copyright (c) 2003-2004, Luke T. Maxon

/********************************************************************************************************************
'
' Copyright (c) 2003-2004, Luke T. Maxon
' All rights reserved.
'
' Redistribution and use in source and binary forms, with or without modification, are permitted provided
' that the following conditions are met:
'
' * Redistributions of source code must retain the above copyright notice, this list of conditions and the
' 	following disclaimer.
'
' * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
' 	the following disclaimer in the documentation and/or other materials provided with the distribution.
'
' * Neither the name of the author nor the names of its contributors may be used to endorse or
' 	promote products derived from this software without specific prior written permission.
'
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
' WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
' PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
' ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
' LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
' INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
' OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
' IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'
'*******************************************************************************************************************/

#endregion

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Forms;

namespace SuedeTest.Core {
   using EventMap = Dictionary<string, Delegate>;

   internal class SupportedEventsRegistry : ILoggable {
      private const string PropertyAssert = "PropertyAssert";

      //table contains:
      //  key = type of a control
      //  value = hashtable where
      //           key = name of event
      //           value = delegate method listening to that event.
      private Dictionary<Type, EventMap> eventTable = new Dictionary<Type, EventMap>();
      
      public event LoggingEventDelegate LoggingOccured;

      protected virtual void OnLoggingOccured(string s) {
         if (LoggingOccured != null) {
            LoggingOccured(s);
         }
      }

      public MulticastDelegate EventHandler(Type type, string eventName) {
         EventMap events;
         Type thisType = type;
         bool found;

         do {
            found = eventTable.TryGetValue(thisType, out events);
            thisType = thisType.BaseType;
         } while (!found && thisType != typeof(object));

         if (events == null)
            return null;

         Delegate output;
         if (events.TryGetValue(eventName, out output))
            return (MulticastDelegate)output;
         else
            return null;
      }

      public EventHandler PropertyAssertHandler(Type type) {
         return (EventHandler)EventHandler(type, PropertyAssert);
      }

      public SupportedEventsRegistry() {
         Censor.Add("mscorlib");
         Censor.Add("System");
         Censor.Add("System.Windows.Forms");
         Censor.Add("System.Drawing");
         Censor.Add("SuedeTest.ProcessHost");
         Censor.Add("SuedeTest.VSPlugin");
         Censor.Add("SuedeTest.FormExplorer");
      }

      /// <summary>
      /// Initializes the supported events registry; adds all types in the current AppDomain
      /// that implement the IRecorder interface to the internal registry of recorders.
      /// </summary>
      /// <param name="listener"></param>
      public void Init(Listener listener) {
         foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
            if (Censor.Contains(assembly.FullName.Substring(0, assembly.FullName.IndexOf(","))))
               continue;
            try {
               foreach (Type type in assembly.GetTypes()) {
                  if ((type.GetInterface("IRecorder") != null) && !type.IsAbstract) {

                     OnLoggingOccured("Adding IRecorder \"" + type.ToString() + "\" to registry...");
                     AddRecorderToRegistry(type, listener);
                  }
               }
            } catch (ReflectionTypeLoadException ex) {
               string inner = ex.InnerException == null ? "" : ex.InnerException.ToString();
               string loader = ex.LoaderExceptions == null ? "" : ex.LoaderExceptions[0].ToString();
               OnLoggingOccured("Unable to load assembly: " + assembly.FullName + System.Environment.NewLine +
               ex.ToString() + System.Environment.NewLine +
               inner + System.Environment.NewLine +
               loader);
            }
         }
      }

      /// <summary>
      /// Creates a recorder of type 'type', and adds it to the listener registry so that
      /// it can receive events.
      /// </summary>
      /// <param name="type"></param>
      /// <param name="listener"></param>
      private void AddRecorderToRegistry(Type type, Listener listener) {
         IRecorder recorder = (IRecorder)Activator.CreateInstance(type, new object[] { listener });

         EventMap events = AddEventHandlersToRegistry(recorder);
         eventTable.Add(recorder.RecorderType, events);
      }

      /// <summary>
      /// Returns a map of event names and handlers for a recorder
      /// </summary>
      /// <param name="recorder"></param>
      private EventMap AddEventHandlersToRegistry(IRecorder recorder) {
         EventMap events = new EventMap();

         ICollection<string> eventNames = Types.GetEventNames(recorder.RecorderType);
         foreach (MethodInfo info in recorder.GetType().GetMethods()) {

            //check if there is an event for the control being recorded
            //that corresponds with this method name
            if (!eventNames.Contains(info.Name))
               continue;

            Delegate handler = Delegate.CreateDelegate(EventHandlerType(info, recorder), recorder, info.Name, false);
            events.Add(info.Name, handler);
            OnLoggingOccured("\t" + info.Name);
         }

         return events;
      }

      /// <summary>
      /// Returns the type of the event-handler.
      /// </summary>
      /// <param name="info">MethodInfo for the event-handler</param>
      /// <param name="recorder">Recorder for the event's object</param>
      /// <returns></returns>
      private Type EventHandlerType(MethodInfo info, IRecorder recorder) {
         EventInfo einfo = recorder.RecorderType.GetEvent(info.Name);
         Type handlerType = einfo.EventHandlerType;

         return handlerType;
      }

   }
}